﻿/**
* @description  Uses a water-like effect with Perlin noise and displacement to fade in or out an image.
*/
package com.foed.animation.transitions {

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.filters.DisplacementMapFilter;
	import flash.filters.DisplacementMapFilterMode;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	import com.foed.animation.Animator;
	
	public class WaterTransition extends Transition {
	
		private var _waterImage:BitmapData;
		private var _noiseImage:BitmapData;
		private var _waterBitmap:Bitmap;
		private var _seed:Number;
		private var _offsets:Array;
		private var _displaceFactor:Number;
		private var _displaceRate:Number;
		private var _alphaRate:Number;
		private var _waterRate:Number = 15;
		private var _maxDisplace:Number = 200;
		
		/**
		* @description  Constructor.
		*
		* @param  pClip  The clip on which to perform the transition.
		* @param  pStartTransform  The starting values for the color transition.
		* @param  pEndTransform  The destination values for the color transition.
		* @param  pTime  The length in milliseconds for the transition.
		* @param  pType  The type of transition. IN moves from startTransform to endTransfom. OUT does the opposite.
		* @param  pEaseFunction  The function to use to transform the transition values over time.
		*/
		public function WaterTransition(
			pClip:DisplayObject=null,
			pStartTransform:Object=null,
			pEndTransform:Object=null,
			pTime:Number=1000,
			pType:String=null,
			pEaseFunction:Function=null
		) {
			_offsets = [new Point(), new Point()];
			_seed = Number(new Date());
			super(pClip, pTime, 0, 1, pType, pEaseFunction);
		}

		/**
		* @description  Called each iteration of the tween by the TweenRunner to transform values.
		*
		* @param  pChangeValues  The current values of the transition at each iteration of the tween.
		*/
		override protected function runTransition(pChangeValues:Object):void {
			for (var i:uint = 0; i < 2; ++i) {
				_offsets[i].x += _waterRate;
				_offsets[i].y -= _waterRate;
			}
			_noiseImage.perlinNoise(100, 100, 1, _seed, false, true, 1, false, _offsets);
			var pMap:DisplacementMapFilter = new DisplacementMapFilter(
				_noiseImage, new Point(), 1, 1, _displaceFactor, _displaceFactor, DisplacementMapFilterMode.CLAMP);
			_waterBitmap.filters = [pMap];
			if (type == TransitionType.IN) {
				if (_waterBitmap.alpha < 1) _waterBitmap.alpha += _alphaRate;
				if (_displaceFactor > 0) {
					_displaceFactor -= _displaceRate;
				}
			} else {
				if (_waterBitmap.alpha > 0) _waterBitmap.alpha -= _alphaRate;
				if (_displaceFactor < _maxDisplace) {
					_displaceFactor += _displaceRate;
				}
			}
			super.runTransition(pChangeValues);
		}
	
		/**
		* @description  Prepares clip to have transitions applied by adding new parent clip.
		*/
		override protected function prepare():void {
			super.prepare();
			_displaceFactor = (type == TransitionType.IN) ? _maxDisplace : 0;
			var pWidth:Number = clip.width;
			var pHeight:Number = clip.height;
			_noiseImage = new BitmapData(pWidth*1.2, pHeight*1.2, false, 0xFFFFFFFF);
			_waterImage = new BitmapData(pWidth*1.2, pHeight*1.2, true, 0x00FFFFFF);
			var pTempImage:BitmapData = new BitmapData(pWidth, pHeight, true, 0x00FFFFFF);
			pTempImage.draw(clip);
			_waterImage.copyPixels(pTempImage, new Rectangle(0, 0, pWidth, pHeight), new Point(pWidth/10, pHeight/10));
			_waterBitmap = new Bitmap(_waterImage);
			_clipHolder.addChild(_waterBitmap);
			_waterBitmap.x = -pWidth/10;
			_waterBitmap.y = -pHeight/10;
			pTempImage.dispose();
			_waterBitmap.alpha = ((type == TransitionType.IN) ? 0 : 1);
			clip.visible = false;
			var pNumInts:Number = time/Animator.intervalTime;
			_displaceRate = _maxDisplace/pNumInts;
			_alphaRate = 1/pNumInts;
		}

		/**
		* @description  Removes any artifacts of the transition.
		*/
		override protected function cleanUp():void {
			super.cleanUp();
			_waterImage.dispose();
		}

	}
	
}